package com.meme.editor.gui.sprite2d;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.SystemColor;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;

import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

import org.apache.log4j.Logger;

import com.meme.app.AppRegistry;
import com.meme.editor.event.EntityChangedEvent;
import com.meme.editor.event.EntityObjectListener;
import com.meme.editor.gui.PropertyEditPanel;
import com.meme.editor.gui.sprite2d.ModuleGroupView_Bird.ReplaceModuleInPartAction;
import com.meme.editor.model.sprite2d.ModuleImage;
import com.meme.editor.model.sprite2d.ModulePart;
import com.meme.editor.model.sprite2d.ModuleRef;
import com.meme.editor.model.sprite2d.Tiled2dPalette;
import com.meme.editor.model.sprite2d.Tiled2dModule;

public class Tiled2dPaletteView  extends JPanel 
		implements EntityObjectListener, AncestorListener
				   , MouseListener, MouseMotionListener, MouseWheelListener
{
	static Logger logger = Logger.getLogger (Tiled2dPaletteView.class);
	
	
	
	static final int MaxScale = 3;
	static final int MinScale = 1;
	static final int DefaultScale = 1;
	// for edit mode
	int mScale = DefaultScale;

	int mImageLeft; // with scale
	int mImageTop;
	
	int mImageWidth; // without scale
	int mImageHeight;
	
	PropertyEditPanel mCellPropertyEditPanel;
	Tiled2dPalette mTiled2dPalette;
	
	Tiled2dGridSelectedRegion mTiled2dGridSelectedRegion;

	ModuleRef mSelectedModulePart;

	public Tiled2dPaletteView (Tiled2dPalette tiled2dGrid)
	{
		mTiled2dPalette = tiled2dGrid;
		mTiled2dGridSelectedRegion = new Tiled2dGridSelectedRegion ();
		

		addAncestorListener (this);
		addMouseListener    (this);
		addMouseMotionListener (this);
		addMouseWheelListener (this);
	}
	
	public void setCellPropertyEditPanel (PropertyEditPanel modulePropertyPanel) 
	{
		mCellPropertyEditPanel = modulePropertyPanel;
	}

	@Override
	public void paintComponent (Graphics _g)
	{
		Graphics2D g = (Graphics2D)_g;
		updateSomeValues ( mScale, 0, 0);
		
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();
		
		
		Color defaultColor = g.getColor ();

		g.setColor ( SystemColor.window );
		g.fillRect(0, 0, viewWidth, viewHeight);
		
		Color lineColor = Color.gray; //new Color(0x7f7f7f7f, true);
		Color selectedColor = new Color(0x7f0000ff, true);
		FontMetrics fm = g.getFontMetrics();
		int strHeight = fm.getHeight();
		String str;
		
		if (mTiled2dPalette != null)
		{
			int cellRows = mTiled2dPalette.getCellRows();
			int cellCols = mTiled2dPalette.getCellCols();
			int cellWidth = mTiled2dPalette.getCellWidth();
			int cellHeight = mTiled2dPalette.getCellHeight();

			int originX = mImageLeft + viewWidth / 2;
			int originY = mImageTop + viewHeight / 2;

			int w = cellWidth * mScale;
			int h = cellHeight * mScale;

			for (int row = 0; row < cellRows; ++ row)
			{
				str = row + " ";
				g.setColor (defaultColor);
				g.drawString(str, originX - fm.stringWidth(str), originY + h * row + h / 2 + strHeight / 2 - fm.getDescent() );
				
				str = " " + row;
				g.setColor (defaultColor);
				g.drawString(str, originX + w * cellCols, originY + h * row + h / 2 + strHeight / 2 - fm.getDescent() );

				for (int col = 0; col < cellCols; ++ col)
				{
					int x = originX + w * col;
					int y = originY + h * row;
					
					Tiled2dModule tile = mTiled2dPalette.getTiled2dModuleByPos(row, col);
					tile.render(g, x, y, false, false, mScale, 0);

					
					if (tile == mSelectedModulePart)
					{
						g.setColor (selectedColor);
						g.fillRect(x, y, w + 1, h + 1);
					}
						
					g.setColor (lineColor);
					g.drawRect(x, y, w, h);

					if (row == 0)
					{
						str = "" + col;
						y -= fm.getDescent() + strHeight / 2;
						x += (w - fm.stringWidth(str)) / 2;
						g.setColor (defaultColor);
						g.drawString(str, x, y );
					}
					if (row == cellRows - 1)
					{
						str = "" + col;
						y += h + strHeight;
						x += (w - fm.stringWidth(str)) / 2;
						g.setColor (defaultColor);
						g.drawString(str, x, y );
					}
				}
			}
			
			if ( mSelectedModulePart != null)
			{
				g.setColor(Color.red);
				int rx = originX + mTiled2dGridSelectedRegion.mLeft * w;
				int ry = originY + mTiled2dGridSelectedRegion.mTop * h;
				int rw = (mTiled2dGridSelectedRegion.mRight - mTiled2dGridSelectedRegion.mLeft + 1) * w - 1;
				int rh = (mTiled2dGridSelectedRegion.mBottom - mTiled2dGridSelectedRegion.mTop + 1) * h - 1;
				g.drawRect(rx, ry, rw, rh);
			}
		}
	}
	
	protected void updateSomeValues (int newScale, int dx, int dy)
	{
		float ratioX;
		float ratioY;
		
		if ( mImageWidth != 0 )
			ratioX = (float) mImageLeft / (float) ( mImageWidth * mScale );
		else
			ratioX = - 0.5f;
		
		if ( mImageHeight != 0 )
			ratioY = (float) mImageTop / (float) ( mImageHeight * mScale );
		else
			ratioY = - 0.5f;
		
		mScale = newScale;
		
		if ( mTiled2dPalette != null )
		{
			mImageWidth  = mTiled2dPalette.getCellWidth() * mTiled2dPalette.getCellCols();
			mImageHeight = mTiled2dPalette.getCellHeight() * mTiled2dPalette.getCellRows();

			mImageLeft = (int) ( ratioX * (mImageWidth  * mScale) );
			mImageTop  = (int) ( ratioY * (mImageHeight * mScale) );
		}
		else
		{
			mImageLeft = 0;
			mImageTop  = 0;
			mImageWidth  = 0;
			mImageHeight = 0;
			
			return;
		}
		
		mImageLeft += dx;
		mImageTop  += dy;
		
		//
		if ( mImageLeft > 0 )
			mImageLeft = 0;
		if ( mImageTop > 0 )
			mImageTop = 0;
		int imgRight  = mImageLeft + (mImageWidth  * mScale);
		int imgBottom = mImageTop  + (mImageHeight * mScale);
		if ( imgRight < 0 )
			mImageLeft -= imgRight;
		if ( imgBottom < 0 )
			mImageTop -= imgBottom;

	}
	
	protected Point lp2dp (Point lp)
	{
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();
		
		Point dp = new Point ();
		
		dp.x = ( mImageLeft + lp.x * mScale ) + viewWidth / 2;
		dp.y = ( mImageTop  + lp.y * mScale ) + viewHeight / 2;
		
		return dp;
	}
	
	protected void _2dp (Point lp)
	{
		Point dp = lp2dp (lp);
		lp.setLocation (dp);
	}
	
	protected Point dp2lp (Point dp)
	{
		int viewWidth = getWidth ();
		int viewHeight = getHeight ();

		Point lp = new Point ();
		
		lp.x = ( dp.x - viewWidth  / 2 - mImageLeft ) / mScale;
		lp.y = ( dp.y - viewHeight / 2 - mImageTop  ) / mScale;
		
		return lp;
	}
	
	protected void _2lp (Point dp)
	{
		Point lp = dp2lp (dp);
		dp.setLocation (lp);
	}

	protected Dimension ds2ls (Dimension size)
	{
		return new Dimension (size.width / mScale, size.height / mScale);
	}
	
	protected void _2ls (Dimension size)
	{
		Dimension ls = ds2ls (size);
		size.setSize(ls);
	}
	protected Dimension ls2ds (Dimension size)
	{
		return new Dimension (size.width * mScale, size.height * mScale);
	}
	
	protected void _2ds (Dimension size)
	{
		Dimension ds = ls2ds (size);
		size.setSize(ds);
	}
	
	// PropertyManagerListener
	@Override 
	public void notifyEntityChanged (EntityChangedEvent event)
	{ 
		repaint ();
	}
	
	// AncestorListener
	@Override
	public void ancestorAdded(AncestorEvent event) 
	{
		if ( mTiled2dPalette != null )
			mTiled2dPalette.addEntityObjectListener (this);

		
		SharedData.sSelectedModuleRef = null;
	}

	@Override
	public void ancestorRemoved(AncestorEvent event)  
	{
		if ( mTiled2dPalette != null )
			mTiled2dPalette.removeEntityObjectListener (this);
		
		SharedData.sSelectedModuleRef = null;
	}
	
	@Override
	public void ancestorMoved(AncestorEvent event) 
	{
		
	}
	
	
	boolean mMovingImage = false;	
	int mLastImageX;
	int mLastImageY;
	Point mRightButtonStartPoint = new Point ();

	void startMovingImage (Point startDevice)
	{
		mMovingImage = true;
		mRightButtonStartPoint.setLocation(startDevice);
		
		mLastImageX = mImageLeft;
		mLastImageY = mImageTop;
	}
	
	void updateMovingImage (Point point)
	{
		  updateSomeValues (mScale, 
				  mLastImageX + point.x - mRightButtonStartPoint.x - mImageLeft,
				  mLastImageY + point.y - mRightButtonStartPoint.y - mImageTop
				  );
		  
			
		  setCursor (Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		  
		  repaint ();
	}
	
	void endMovingImage ()
	{
		setCursor (Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		
		mMovingImage = false;
	}

//--------------------------------------------------------------------------
// as a MouseListener
//--------------------------------------------------------------------------

	void changeSelectedModule (Point dp)
	{
		Point lp = dp2lp (dp);
		
		if ( mTiled2dPalette != null && lp.x >= 0 && lp.y >= 0)
		{
			int cellRows = mTiled2dPalette.getCellRows();
			int cellCols = mTiled2dPalette.getCellCols();
			int cellWidth = mTiled2dPalette.getCellWidth();
			int cellHeight = mTiled2dPalette.getCellHeight();
			
			int row = lp.y / cellHeight;
			int col = lp.x / cellWidth;
			
			if ( row < cellRows && col < cellCols )
			{
				mSelectedModulePart = mTiled2dPalette.getTiled2dModuleByPos(row, col);
				
				setSelectedModuleRef (mSelectedModulePart);
			}
		}
		
		repaint ();
	}
	
	public void setSelectedModuleRef (ModuleRef part)
	{
		mSelectedModulePart = part;
		
//		if ( mChildPropertyView != null )
//			mChildPropertyView.setDataSource (part);
		
		AppRegistry.setRegistry("editor.sprite2d.CompositedModuleView.selectedModulePart", mSelectedModulePart);
		SharedData.sSelectedModuleRef = mSelectedModulePart;
		
		
		if ( part != null )
		{
			if ( mCellPropertyEditPanel != null )
				mCellPropertyEditPanel.setDataSource (part);
		}
		
		repaint ();
	}
	
	boolean mSelectingRegion;
	//Point mLeftButtonStartPoint = new Point ();
	int mStartCellCol;
	int mStartCellRow;
	
	public void startRegionSelecting (Point dp)
	{
		mSelectingRegion = true;
		
		Point lp = dp2lp (dp);
		mStartCellRow = lp.y / mTiled2dPalette.getCellHeight();
		mStartCellCol = lp.x / mTiled2dPalette.getCellWidth ();
		
		mTiled2dGridSelectedRegion.setRegion(mStartCellRow, mStartCellCol, mStartCellRow, mStartCellCol);
		mTiled2dGridSelectedRegion.makeRegionValid (mTiled2dPalette);
	}
	
	public void updateRegionSelcecting (Point dp)
	{
		Point lp = dp2lp (dp);

		mTiled2dGridSelectedRegion.setRegion(mStartCellRow, mStartCellCol, 
				lp.y / mTiled2dPalette.getCellHeight(), 
				lp.x / mTiled2dPalette.getCellWidth ());
		mTiled2dGridSelectedRegion.makeRegionValid (mTiled2dPalette);
		
		repaint ();
	}
	
	public void endRegionSelecting ()
	{
		if ( SharedData.sTiled2dBackgroundView != null )
		{
			SharedData.sTiled2dBackgroundView.setCopySourceBackground (
					mTiled2dGridSelectedRegion.createTiled2dBackgroundFromGridSelectionRegion(mTiled2dPalette) );
		}
		
		mSelectingRegion = false;
	}
	
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseClicked(java.awt.event.MouseEvent e)
	{
		if (e.getButton () == e.BUTTON3)
		{
//			setSelectedModule ( getModuleAtPoint ( e.getPoint() ) );
//			
//			if ( mReadOnly )
//			{
//				JPopupMenu popMenu = new JPopupMenu();
//				AbstractAction action = new ReplaceModuleInPartAction (); 
//				popMenu.add ( action );
//				
//
//				ModuleRef part = SharedData.sSelectedModuleRef;
//				
//				action.setEnabled(part != null && part.checkReferability(mSelectedModule));
//				
//				popMenu.show(this, e.getX(), e.getY());
//			}
		}
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mousePressed(java.awt.event.MouseEvent e)
	{
		//PureModule module = getModuleAtPoint ( e.getPoint() );
		
		if (e.getButton () == e.BUTTON1)
		{
			changeSelectedModule (e.getPoint());
			
			startRegionSelecting (e.getPoint());
		}
		  
		if (e.getButton () == e.BUTTON3)
		{			
			startMovingImage (e.getPoint());
		}
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseReleased(java.awt.event.MouseEvent e)
	{
		if (e.getButton () == e.BUTTON1)
		{
			if (mSelectingRegion)
			{
				updateRegionSelcecting (e.getPoint());			
				endRegionSelecting ();
			}
		}
		  
		if (e.getButton () == e.BUTTON3)
		{			
			if (mMovingImage)
			{
				updateMovingImage (e.getPoint());
				endMovingImage ();
			}

		}  
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseEntered(java.awt.event.MouseEvent e)
	{
		
	}
	  
	// Method descriptor #12 (Ljava/awt/event/MouseEvent;)V
	public void mouseExited(java.awt.event.MouseEvent e)
	{
		
	}
	  
//--------------------------------------------------------------------------
// as a MouseMotionListener
//--------------------------------------------------------------------------

	// Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	public void mouseDragged(java.awt.event.MouseEvent e)
	{
		if (mMovingImage)
			updateMovingImage (e.getPoint());
		
		if (mSelectingRegion)
			updateRegionSelcecting (e.getPoint());
	}
	  
	  // Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	public void mouseMoved(java.awt.event.MouseEvent e)
	{

	}
	  
//--------------------------------------------------------------------------
// as a MouseWheelListener
//--------------------------------------------------------------------------

	// Method descriptor #8 (Ljava/awt/event/MouseWheelEvent;)V
	public void mouseWheelMoved(java.awt.event.MouseWheelEvent event)
	{
		float scale = (float)mScale + ( event.getWheelRotation () > 0 ? 1 : -1 );
		
		if ( scale < MinScale)
			scale = MinScale;
		if (scale > MaxScale)
			scale = MaxScale;

		updateSomeValues ((int)scale, 0, 0);
		repaint ();
	}
	  
	
//--------------------------------------------------------------------------
	
	class ReplaceModuleInPartAction extends AbstractAction 
	{
		public ReplaceModuleInPartAction ()
		{
			super ("Replaced by This One");
		}
		
	    public void actionPerformed(ActionEvent e) 
	    {
//	    	ModuleRef part = SharedData.sSelectedModuleRef;
//			part.setModule (mSelectedModule);
//			part.notifyModified();
	    }
	}
}
